
import fs from 'fs';
import { tools } from './tools/tools';
import { image_processor } from './image_processor';

export namespace res_genrator {

    export class ResGenrator {
        constructor() {
            this.typeCfg = tools.TypeCfg();
            this.generalCfg = tools.GenricCfg();
        }

        public async Load(root: string, withImgPrc: boolean) {
            this.curRoot = root;

            let cfgs = fs.readFileSync(this.PathGenrator(`${this.generalCfg.cfg_path}/${this.generalCfg.cfg_file}`), {
                encoding: 'utf-8'
            });
            this.CfgAnalyzer(JSON.parse(cfgs), withImgPrc);
        }

        private async CfgAnalyzer(cfgs: tools.GENRATOR_CFG_TYPE[], withImgPrc?: boolean): Promise<void> {
            console.log('***res_genrator start***');
            for (let cfg of cfgs) {
                await this.CfgLoader(cfg, withImgPrc);
            }
            console.log('***res_genrator end***');
        }

        private async CfgLoader(cfg: tools.GENRATOR_CFG_TYPE, withImgPrc?: boolean): Promise<void> {

            if (!cfg) {
                return;
            }

            console.log(`---curCfg: ${cfg.name}---`);

            let time: number = Date.now();
            console.log(`golb file start: ${tools.Time()}`);
            let fileArr = await tools.GlobFiles(cfg.include, cfg.exclude, this.PathGenrator(cfg.res_path), { nodir: true });
            console.log(`golb file end:   ${tools.Time()}, ${((Date.now() - time) / 1000) / 1000}s`);

            if (withImgPrc) {
                let sheetDic = this.AssetsCheck(fileArr, this.PathGenrator(cfg.res_path));
                if (!!sheetDic) {
                    let img_processor = new image_processor.ImageProcessor(this.curRoot);
                    for (let path of Object.keys(sheetDic)) {
                        await img_processor.Process(this.PathGenrator(tools.QueryEgretProperties(this.curRoot)?.path || tools.DEF_RES_PATH), sheetDic[path]);
                    }
                }
            }

            time = Date.now();
            console.log(`Resource analyze start: ${tools.Time()}`);
            let dic = await this.ResourcesAnalyzer(cfg.res_path, fileArr, cfg.rename);
            console.log(`Resource analyze end:   ${tools.Time()}, ${(Date.now() - time) / 1000}s`);

            time = Date.now();
            console.log(`Resource check start: ${tools.Time()}`);

            let nameDic: { [name: string]: tools.RES_TYPE } = {};
            let urlDic: { [url: string]: string } = {};
            let name: string;
            for (let url in dic) { //检查一下
                for (let res of dic[url]) {
                    name = res.name;
                    if (!nameDic[name]) {
                        nameDic[name] = res;
                        urlDic[url] = name;
                    } else {
                        console.warn(`File: ${cfg.outputdir}: Duplicate files found, right one will be skipped, Name: ${name}\n` +
                            `\tPath: ${this.PathGenrator(nameDic[name]['url'])}<->${this.PathGenrator(url)}`);
                    }
                }
            }

            let destArr: tools.RES_TYPE[] = [];
            for (let name in nameDic) { //转换成目标类型
                destArr.push(nameDic[name]);
            }

            destArr.sort((a, b) => {
                return tools.HashNum(b.name) - tools.HashNum(a.name);
            });
            console.log(`Resource check end:   ${tools.Time()}, ${(Date.now() - time) / 1000}s`);

            time = Date.now();
            console.log(`Group analyze start: ${tools.Time()}`)
            let group_mid_cfg = await this.GroupAnalizer(cfg.res_path, cfg.group_cfg);
            let group_cfg: tools.GROUP_TYPE[] = [];
            let group: tools.GROUP_MID_TYPE;
            for (let i = 0, L = group_mid_cfg.length; i < L; ++i) { //组里面的文件检查一下
                group = group_mid_cfg[i];
                if (group.keys.length < 1) { //清理掉
                    console.warn(`File: ${cfg.outputdir}: GroupName: ${group.name} has 0 config files`);
                    group_mid_cfg.splice(i--, 1);
                    --L;
                    continue;
                }
                let groups: string[] = [];
                for (let url of group.keys) {
                    if (!!url && !!urlDic[url]) {
                        groups.push(urlDic[url]);
                    } else {
                        console.warn(`File: ${cfg.outputdir}: Can not found file in resource, GroupName: ${group.name}, resourceName: ${url}`);
                    }
                }

                groups.sort((a, b) => {
                    return tools.HashNum(b) - tools.HashNum(a);
                });

                group_cfg.push(
                    {
                        name: group.name,
                        keys: groups.join(',')
                    }
                );

            }
            console.log(`Group analyze end:   ${tools.Time()}, ${(Date.now() - time) / 1000}s`);

            time = Date.now();
            console.log(`Write file start: ${tools.Time()}`);
            this.FileWriter(this.PathGenrator(`${cfg.res_path}/${cfg.outputdir}`), JSON.stringify({
                resources: destArr,
                groups: group_cfg
            }, null, 4));
            console.log(`Write file end:   ${tools.Time()}, ${(Date.now() - time) / 1000}s`);
        }

        private AssetsCheck(fileArr: string[], root: string): { [path: string]: tools.IMG_SHEET_TYPE } {
            let sheetCfg: { [path: string]: tools.IMG_SHEET_TYPE } = {};
            for (let file of fileArr) {
                if (/config.json$/.test(file)) {
                    try {
                        let config: tools.IMG_SHEET_TYPE = JSON.parse(fs.readFileSync(file, { encoding: 'utf-8' }));
                        if (config.hasOwnProperty('filemarker') && Object.getOwnPropertyDescriptor(config, 'filemarker')?.value == "config_img_sheet") { //图集配置

                            let path = file.match(/[\s\S]*(?=\/config.json$)/);
                            if (path) {
                                sheetCfg[path[0]] = config;
                                fileArr.splice(fileArr.indexOf(file), 1); //图集配置文件清理

                                config.cfg_dir = file; //方便后面用

                                let propertyDescriptor = Object.getOwnPropertyDescriptor(config, 'include');
                                if (!!propertyDescriptor) {
                                    let subImg: string;
                                    let index: number;
                                    for (let i = 0, L = propertyDescriptor.value.length; i < L; ++i) {
                                        subImg = propertyDescriptor.value[i];
                                        index = fileArr.indexOf(`${path[0]}/${subImg}`);
                                        if (index < 0) { //不在resource里面，清理一下
                                            propertyDescriptor?.value.splice(i--, 1);
                                            --L;
                                        }
                                        fileArr.splice(index, 1);  //图集里面的只存在于图集就好了
                                        propertyDescriptor.value[i] = `${path[0]}/${subImg}`;
                                    }

                                }
                                fileArr.push(`${root}/${config.output_dir}/${config.output_file}.json`);
                            }
                        }
                    } catch (e) {
                        continue;
                    }
                }
            }
            return sheetCfg;
        }

        private async ResourcesAnalyzer(resPath: string, paths: string[], rename?: { [url: string]: string[] }): Promise<{ [path: string]: tools.RES_TYPE[] }> {
            if (!paths || paths.length == 0) {
                return {};
            }

            let tmpFileDic: { normal: { [path: string]: tools.RES_TYPE[] }, extPath: string[] } = {
                normal: {},
                extPath: []
            };

            this.ResourceGenrator(resPath, paths, tmpFileDic, rename);
            for (let sheetPath of tmpFileDic.extPath) {
                delete tmpFileDic.normal[sheetPath];
            }
            return tmpFileDic.normal;
        }

        private ResourceGenrator(resPath: string, paths: string[],
            dic: { normal: { [path: string]: tools.RES_TYPE[] }, extPath: string[] }, rename?: { [url: string]: string[] }) {

            let typeDic = this.typeCfg;
            let tmp: string[];
            let fileName: string;
            let path: string;
            let type: string;
            let jsonFile: Object;
            let choper: RegExpMatchArray | null;
            let subFix: string;
            let subkeys: string;
            let fileReg = RegExp(`(?<=${this.PathGenrator(resPath).replace('/', '\\/')}\\/)[\\s\\S]*`); // /(?<=..\/resource\/)[\s\S]*/;

            for (let fullPath of paths) {
                if (fs.statSync(fullPath).isDirectory()) {
                    console.log(`A folder is detected in the configuration, Path: ${fullPath}`);
                    continue;
                }
                tmp = fullPath.split('/');
                fileName = tmp[tmp.length - 1];
                choper = fullPath.match(fileReg);
                path = !choper ? '' : choper[0];
                subFix = fileName.substring(fileName.lastIndexOf('.') + 1);
                type = typeDic[subFix];
                subkeys = '';

                if (type == 'json' || type == 'JSON') {
                    try {
                        jsonFile = JSON.parse(fs.readFileSync(fullPath, 'utf-8').replace(/^\uFEFF/, ''));
                        if (jsonFile.hasOwnProperty('file') && jsonFile.hasOwnProperty('frames')) {//筛一下图集……
                            type = 'sheet';
                            if (jsonFile.hasOwnProperty('file')) {
                                let imgPath = path.slice(0, path.lastIndexOf('/') - 1) + Object.getOwnPropertyDescriptor(jsonFile, 'file')!.value;
                                dic.extPath.push(imgPath);
                            }
                            let descriptor: PropertyDescriptor | undefined = Object.getOwnPropertyDescriptor(jsonFile, 'frames');
                            for (let file in descriptor?.value) {
                                subkeys += !!subkeys ? `,${file}` : file;
                            }

                        }

                        if (jsonFile.hasOwnProperty('resources') && jsonFile.hasOwnProperty('groups')) { //资源文件
                            type = 'rescfg';
                        }
                    }
                    catch (e) {
                        console.error(`JSON error, Path: ${this.PathGenrator(path)}`);
                    }
                } else if ('font' == type || 'FONT' == type) {
                    try {
                        jsonFile = JSON.parse(fs.readFileSync(fullPath, 'utf-8').replace(/^\uFEFF/, ''));
                        if (jsonFile.hasOwnProperty('file')) {
                            let imgPath = path.slice(0, path.lastIndexOf('/') - 1) + Object.getOwnPropertyDescriptor(jsonFile, 'file')!.value;
                            dic.extPath.push(imgPath);
                        }
                    }
                    catch (e) {
                        console.error(`JSON error, Path: ${this.PathGenrator(path)}`);
                    }
                }

                !dic.normal[path] && (dic.normal[path] = []);
                if (!!rename && !!rename[path]) {
                    for (let name of rename[path]) {
                        let tmpData: tools.RES_TYPE = {
                            name: name,
                            type: type,
                            url: path
                        }
                        subkeys && (tmpData.subkeys = subkeys);
                        dic.normal[path].push(tmpData);
                    }
                } else {
                    let name: string = fileName.replace(RegExp(`.(?=${subFix}$)`), '_');
                    let tmpData: tools.RES_TYPE = {
                        name: name,
                        type: type,
                        url: path
                    };
                    subkeys && (tmpData.subkeys = subkeys);
                    dic.normal[path].push(tmpData);
                }

            }
        }

        private async GroupAnalizer(resPath: string, path: string): Promise<tools.GROUP_MID_TYPE[]> {
            let files = fs.readdirSync(this.PathGenrator(`${this.generalCfg.cfg_path}/${path}`));
            if (!files || files.length < 0) {
                return []
            }
            let tmpGroup: tools.GROUP_MID_TYPE[] = [];
            let group_cfg: tools.GROUP_CFG_TYPE;
            let fullPath: string;

            let matchArr: RegExpMatchArray | null;

            let include: string[];

            for (let cfgFile of files) {
                fullPath = this.PathGenrator(`${this.generalCfg.cfg_path}/${path}/${cfgFile}`);
                if (fs.statSync(fullPath).isDirectory()) {
                    console.log(`A folder is detected in the configuration, Path: ${fullPath}`);
                    continue;
                }
                group_cfg = JSON.parse(fs.readFileSync(fullPath, {
                    encoding: 'utf-8'
                }));

                if (!group_cfg.include) {
                    console.log(`wrong config format: ${fullPath}`);
                    continue;
                }

                include = await tools.GlobFiles(group_cfg.include, group_cfg.exclude, undefined, { nodir: true, cwd: this.PathGenrator(resPath) });
                matchArr = cfgFile.match(/[\s\S]*(?=\.)/);
                tmpGroup.push({
                    name: (matchArr && matchArr[0]) || '',
                    keys: include
                });
            }

            return tmpGroup;
        }

        // private GroupGenrator(matchFiles: string[]): string[] {
        //     let tmp;
        //     let fileName;
        //     let tmpkey: string[] = [];
        //     let subFix: string;
        //     for (let fullPath of matchFiles) {
        //         if (fs.statSync(fullPath).isDirectory()) {
        //             console.log(`A folder is detected in the configuration, Path: ${fullPath}`);
        //             continue;
        //         }
        //         tmp = fullPath.split('/');
        //         fileName = tmp[tmp.length - 1];
        //         subFix = fileName.substring(fileName.lastIndexOf('.') + 1);
        //         fileName = fileName.replace(RegExp(`.(?=${subFix}$)`), '_');
        //         tmpkey.indexOf(fileName) < 0 && tmpkey.push(fileName);
        //     }
        //     return tmpkey;
        // }


        private FileWriter(path: string, content: string) {
            fs.writeFileSync(path, content, {
                encoding: 'utf-8'
            });
        }

        private PathGenrator(tmpPath: string): string {
            return `${this.curRoot}/${tmpPath}`;
        }

        private curRoot: string = '';

        private typeCfg: { [type: string]: string };
        private generalCfg: tools.GENERAL_CFG_TYPE;
    }
}
